package org.openmrs.module.simplelabentry.util;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.openmrs.Cohort;
import org.openmrs.Concept;
import org.openmrs.Encounter;
import org.openmrs.Order;
import org.openmrs.OrderType;
import org.openmrs.Patient;
import org.openmrs.PatientProgram;
import org.openmrs.PatientState;
import org.openmrs.Program;
import org.openmrs.ProgramWorkflow;
import org.openmrs.api.context.Context;
import org.openmrs.module.hospitalcore.model.Lab;
import org.openmrs.module.simplelabentry.SimpleLabEntryService;
import org.openmrs.module.simplelabentry.report.DataSet;
import org.openmrs.module.simplelabentry.report.DataSetRow;

public class SimpleLabEntryUtil {

    private static Log log = LogFactory.getLog(SimpleLabEntryUtil.class);
    public static String REPLACE_TREATMENT_GROUP_NAMES = "PEDIATRIC=PEDI,FOLLOWING=FOL,GROUP =,PATIENT TRANSFERRED OUT=XFER,PATIENT DIED=DIED";

    public static SimpleLabEntryService getSimpleLabEntryService() {
        return (SimpleLabEntryService) Context.getService(SimpleLabEntryService.class);
    }

//    /**
//     * Gets the lab order type associated with the underlying lab order type global property.
//     *
//     * @return
//     */
//    public static OrderType getLabOrderType() {
//        return (OrderType) getGlobalPropertyValue("simplelabentry.labOrderType");
//    }

//    public static PatientIdentifierType getPatientIdentifierType() {
//        return (PatientIdentifierType) getGlobalPropertyValue("simplelabentry.patientIdentifierType");
//    }

    public static List<Program> getLabReportPrograms() {
        List<Program> programs = new LinkedList<Program>();
        programs.add(Context.getProgramWorkflowService().getProgramByName("HIV PROGRAM"));
        //programs.add(Context.getProgramWorkflowService().getProgramByName("PMTCT PROGRAM"));
        programs.add(Context.getProgramWorkflowService().getProgramByName("PEDIATRIC HIV PROGRAM"));
        //programs.add(Context.getProgramWorkflowService().getProgramByName("TUBERCULOSIS PROGRAM"));
        return programs;
    }

//    /**
//     * Gets the lab order type associated with the underlying lab order type global property.
//     *
//     * FIXME Obviously this is a hack, but it's better than having the code to get these properties
//     * copied in different locations.
//     *
//     * @return
//     */
//    public static Object getGlobalPropertyValue(String property) {
//
//        // Retrieve proper OrderType for Lab Orders
//        Object object = null;
//        String identifier = Context.getAdministrationService().getGlobalProperty(property);
//
//        try {
//            if ("simplelabentry.labOrderType".equals(property)) {
//                object = (OrderType) Context.getOrderService().getOrderType(Integer.valueOf(identifier));
//            } else if ("simplelabentry.labTestEncounterType".equals(property)) {
//                object = (EncounterType) Context.getEncounterService().getEncounterType(Integer.valueOf(identifier));
//            } else if ("simplelabentry.patientIdentifierType".equals(property)) {
//                object = (PatientIdentifierType) Context.getPatientService().getPatientIdentifierType(Integer.valueOf(identifier));
//            }
//
//        } catch (Exception e) {
//            log.error("error: ", e);
//
//        }
//
//        if (object == null) {
//            throw new RuntimeException("Unable to retrieve object with identifier <" + identifier + ">.  Please specify an appropriate value for global property '" + property + "'");
//        }
//
//        return object;
//    }

    public static Cohort getCohort(List<Encounter> encounters) {
        // Get cohort of patients from encounters
        Cohort patients = new Cohort();
        for (Encounter encounter : encounters) {
            patients.addMember(encounter.getPatientId());
        }
        return patients;
    }

    /**
     * This is required because columns need to be in a specific order.
     * @return
     */
    public static List<Concept> getLabReportConcepts() {

       SimpleLabEntryService service = Context.getService(SimpleLabEntryService.class);
       return new ArrayList<Concept>(service.getCurrentLab().getInvestigationsToDisplay());
    }

    /**
     *
     * @param encounters
     * @return
     */
    public static Map<Integer, String> getTreatmentGroupCache(Cohort patients) {
        Map<Integer, String> treatmentGroupCache = new HashMap<Integer, String>();

        if (!patients.isEmpty()) {
            // Loop over every program - does not do PMTCT PROGRAM because
            for (Program program : SimpleLabEntryUtil.getLabReportPrograms()) {
                // Get patient programs / treatment groups for all patients
                Map<Integer, PatientProgram> patientPrograms =
                        Context.getPatientSetService().getPatientPrograms(patients, program);

                for (PatientProgram patientProgram : patientPrograms.values()) {

                    // We only need to lookup the treatment group in the case that a
                    // patient does not already have a treatment group placed in the cache
                    String treatmentGroup =
                            treatmentGroupCache.get(patientProgram.getPatient().getPatientId());

                    if (treatmentGroup == null) {
                        // FIXME Hack to get the treatment group for either HIV PROGRAM, PEDIATRIC PROGRAM, or TUBERCULOSIS PROGRAM
                        ProgramWorkflow workflow = program.getWorkflowByName("TREATMENT GROUP");
                        if (workflow == null) {
                            workflow = program.getWorkflowByName("TUBERCULOSIS TREATMENT GROUP");
                        }
                        if (workflow == null) {
                            continue;	// if we can't find a workflow at this point we just move to the next patient
                        }
                        // Get the patient's current state based
                        PatientState patientState = patientProgram.getCurrentState(workflow);
                        if (patientState != null) {
                            // TODO This needs to be more generalized since not everyone will use the Rwanda
                            // convention for naming groups
                            // Show only the group number
                            String value = patientState.getState().getConcept().getDisplayString();

                            if (value != null) {
                                value = SimpleLabEntryUtil.replace(value, REPLACE_TREATMENT_GROUP_NAMES);
                            }
                            treatmentGroupCache.put(patientProgram.getPatient().getPatientId(), value);
                        }
                    }
                }
            }
        }
        return treatmentGroupCache;
    }

    /**
     * Remove all unwanted words from the given string.
     *
     * @param str
     * @param unwanteds
     * @return
     */
    public static String remove(String str, String removeWords) {
        if (removeWords != null) {
            for (String remove : removeWords.split(",")) {
                if (remove != null) {
                    str = str.replace(remove, "");
                }
            }
        }
        return str.trim();

    }

    /**
     * Remove all unwanted words from the given string.
     *
     * TODO Should be handled by a regular expression
     *
     * @param str
     * @param replaceWords
     * @return
     */
    public static String replace(String str, String replaceWords) {
        if (replaceWords != null) {
            // replaceMapping: oldWord=newWord
            for (String replaceMapping : replaceWords.split(",")) {
                String[] replaceArray = replaceMapping.split("=");
                if (replaceArray[0] != null) {
                    if (replaceArray.length >= 2) {
                        str = str.replace(replaceArray[0], replaceArray[1]);
                    } else {
                        str = str.replace(replaceArray[0], "");
                    }
                }
            }
        }
        return str.trim();

    }

    /**
     *
     * @param dataSet
     * @param sortByColumn
     * @return
     */
    public static void sortDataSet(DataSet dataSet) {
        // Create a new list of rows
        List<DataSetRow> rows = new LinkedList<DataSetRow>(dataSet.getRows());

        // Sort rows
        Collections.sort(rows, new Comparator<DataSetRow>() {

            public int compare(DataSetRow row1, DataSetRow row2) {
                return row1.compareTo(row2);
            }
        });
        dataSet.setRows(rows);
    }

    /**
     * Groups the data set by the given column and returns a map
     * of datasets indexed by the specified group by column.
     *
     * @param groupByColumn
     * @return
     */
    public static Map<String, DataSet> groupDataSetByColumn(DataSet dataSet, String groupByColumn) {
        Map<String, DataSet> groupedDataSets = new HashMap<String, DataSet>();
        for (DataSetRow dataRow : dataSet) {
            String groupKey = dataRow.get(groupByColumn);
            DataSet groupDataSet = groupedDataSets.get(groupKey);
            if (groupDataSet == null) {
                groupDataSet = new DataSet();
            }
            groupDataSet.add(dataRow);
            groupedDataSets.put(groupKey, groupDataSet);
        }
        return groupedDataSets;
    }

    /**
     *
     * Returns a list of orders from the last X months by patient.
     *
     * @param p, lastNMonths
     * @return
     */
    public static List<String> getLabOrderIDsByPatient(Patient p, Integer lastNMonths) {
        SimpleLabEntryService ls = Context.getService(SimpleLabEntryService.class);
        Lab lab  = ls.getCurrentLab();
    	OrderType orderType = lab.getLabOrderType();
        List<Order> oList = Context.getOrderService().getOrders(Order.class, Collections.singletonList(p), null, null, null, null, Collections.singletonList(orderType));
        List<String> ret = new ArrayList<String>();
        Calendar c = Calendar.getInstance();
        c.setTime(new Date());
        c.add(Calendar.MONTH, -lastNMonths);
        for (Order o : oList) {
            //System.out.println("o.getStartDate() = "+o.getStartDate() + "after c.getTime() "+c.getTime());
            //System.out.println("o.getAccessionNumber() !=null "+o.getAccessionNumber()!=null);
            if (o.getStartDate() != null) {
                if (o.getStartDate().after(c.getTime()) && o.getAccessionNumber() != null && !o.getAccessionNumber().equals("")) {
                    ret.add(o.getAccessionNumber());
                }
            }
        }
        return ret;
    }
}
